package com.thoughtworks.rhinoextension;

import org.mozilla.javascript.*;
import org.mozilla.javascript.tools.SourceReader;
import org.mozilla.javascript.tools.ToolErrorReporter;
import org.mozilla.javascript.tools.shell.Global;
import org.mozilla.javascript.tools.shell.ShellContextFactory;

import java.io.IOException;

public class RunJSpecTestsAction implements ContextAction {

    private String jsFile;
    private Global global;
    private String sourceFileEncoding;

    public RunJSpecTestsAction(String jsFile, Global global, ShellContextFactory shellContextFactory) {
        this.jsFile = jsFile;
        this.global = global;
        this.sourceFileEncoding = shellContextFactory.getCharacterEncoding();
    }

    public Object run(Context cx) {
        return processFiles(cx, jsFile);
    }

    private Object processFiles(Context cx, String file) {
        return processSource(cx, file);
    }

    private Object processSource(Context cx, String filename) {
        Object result = processFile(cx, this.global, filename);
        System.gc();
        return result;
    }

    private Object processFile(Context cx, Scriptable scope, String filename) {
        String source = readFileOrUrl(filename);
        if (source == null) return null;

        source = elimateComment(source);
        Script script = loadScriptFromSource(cx, source, filename);

        if (script == null) return null;
        return evaluateScript(script, cx, scope);
    }

    private String elimateComment(String source) {
        if (source.length() > 0 && source.charAt(0) == '#') {
            for (int i = 1; i != source.length(); ++i) {
                int c = source.charAt(i);
                if (c == '\n' || c == '\r') {
                    source = source.substring(i);
                    break;
                }
            }
        }
        return source;
    }

    private Script loadScriptFromSource(Context cx, String scriptSource, String path) {
        try {
            return cx.compileString(scriptSource, path, 1, null);
        } catch (EvaluatorException ee) {
        } catch (RhinoException rex) {
            ToolErrorReporter.reportException(cx.getErrorReporter(), rex);
        } catch (VirtualMachineError ex) {
            ex.printStackTrace();
            String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ex.toString());
            Context.reportError(msg);
        }
        return null;
    }

    private Object evaluateScript(Script script, Context cx, Scriptable scope) {
        try {
            return script.exec(cx, scope);
        } catch (RhinoException rex) {
            ToolErrorReporter.reportException(cx.getErrorReporter(), rex);
        } catch (VirtualMachineError ex) {
            ex.printStackTrace();
            String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ex.toString());
            Context.reportError(msg);
        }
        return Context.getUndefinedValue();
    }

    private String readFileOrUrl(String path) {
        try {
            return (String) SourceReader.readFileOrUrl(path, true, sourceFileEncoding);
        } catch (IOException ex) {
            Context.reportError(ToolErrorReporter.getMessage("msg.couldnt.read.source", path, ex.getMessage()));
            return null;
        }
    }
    
}


